﻿#include <iostream>
#include <string>
#include <map>
#include "tcpserver.h"
#include "debug.cpp"
#include "frame_protocol.h"

class CMDManager
{
public:

private:
    const static uint16_t CMD_MAX     = 0xFFF0;
    
    int m_ids[CMD_MAX];
public:
    const static int NOT_BACKEND_ID = 0;
    CMDManager()
    {
		for(int i=0;i<CMD_MAX;i++)
			m_ids[i] = NOT_BACKEND_ID;
    }
    void register_cmd_for_backend( int _id, uint16_t cmd)
    {
        if(cmd >= CMD_MAX )
            return;
        m_ids[cmd] = _id;
        LOGD("register cmd = 0x%x, by %d", cmd, _id);
    }
    void unregister_backend(int _id)
    {
        for(int _t = 0 ; _t < CMD_MAX; _t++)
        {
            if(m_ids[_t] == _id)
            {
                LOGD("unregister cmd = 0x%x, by %d", _t, _id);
                m_ids[_t] = NOT_BACKEND_ID;
            }
        }
    }

    int get_id(uint16_t cmd)
    {
        if(cmd >= CMD_MAX )
            return NOT_BACKEND_ID;
        return m_ids[cmd];
    }
};

class ClientInfo
{
public:
    int id;
    ClientType type;
};

class ClientManager
{
    typedef std::map<int, ClientInfo> ClientMap;
    std::map<int, ClientInfo> client_map;
    int user_auth_backend_id;
public:
    const static int UNKNOW_ID = 0;
    ClientManager()
    {
        user_auth_backend_id = UNKNOW_ID;
    }
    void add_client(int id, ClientInfo &info)
    {
        client_map[id] = info;
    }
    void del_client(int id)
    {
        ClientMap::iterator iter = client_map.find(id);
        if(iter!=client_map.end())
        {
            client_map.erase(iter);
            if (user_auth_backend_id == id)
                user_auth_backend_id = UNKNOW_ID;
        }
        
#ifdef DEBUG        

        LOGD("client size:%d", client_map.size());
        iter = client_map.begin();
        for(;iter != client_map.end();iter++)
        {
            LOGD("active client-> id:%d, type:%d", iter->second.id,iter->second.type);
        }
#endif
    }
    
    bool get_client_info(int id, ClientInfo &info)
    {
        ClientMap::iterator iter = client_map.find(id);
        if(iter!=client_map.end())
        {
            info = iter->second;
            return true;
        }
        return false;
    }
    
    bool set_client_info(int id, ClientInfo &info)
    {
        LOGD("id:%d, clienttype:%d", id, info.type);
        ClientMap::iterator iter = client_map.find(id);
        if(iter!=client_map.end())
        {
            client_map[id] = info;
            if(info.type == CLIENT_TYPE_BUSINESS_USER_AUTH)
                user_auth_backend_id = id;
            return true;
        }
        return false;
    }
    int get_user_auth_backend()
    {
        return user_auth_backend_id;
    }
};
ClientManager client_manager;
CMDManager    cmd_manager;
using namespace std;
using namespace uv;
TCPServer server;
class TestTCPProtocol: public TCPServerProtocolProcess
{

public:
    TestTCPProtocol() {}
    virtual ~TestTCPProtocol() {}
    virtual const StreamBuffer ParsePacket(const NetPacket& packet, const unsigned char* buf,int clientid)
    {
        LOGD("clientid=%d, recv version: 0x%x, cmd: 0x%x, datalen: %d",clientid,packet.version,packet.cmd,packet.datalen);
        ClientInfo info;
        if (false == client_manager.get_client_info(clientid, info))
        {
            LOGE("unknow clientid %d",clientid);
            return StreamBuffer();
        }
        
        if(info.type == CLIENT_TYPE_UNKNOW)
        {
            if(packet.cmd == CMD_REGISTER_BACKEND)
            {
                #ifdef DEBUG
                    printf("+++++%d+++\n",packet.datalen);
                    for(int i=0;i<packet.datalen;i++)
                    {
                        printf("%d,",buf[i]);
                    }
                    printf("\n-----\n");
                #endif

                ClientInfo info;
                if (false == client_manager.get_client_info(clientid, info))
                {
                    LOGW("unknow clientid %d",clientid);
                    return StreamBuffer();
                }
                if(info.type != CLIENT_TYPE_UNKNOW)
                {
                    LOGW("not allow to change client type from %d to %d", info.type, 4);
                    return StreamBuffer();
                }
                
                ASSERT(packet.datalen >= sizeof(struct RegisterBusinessHead),"RegisterBusinessHead is not found");
                struct RegisterBusinessHead * head = (struct RegisterBusinessHead *)buf;
                
                info.type = (ClientType)head->type;
                client_manager.set_client_info(clientid, info);
                
                struct RegisterBusinessBody * body = (struct RegisterBusinessBody *)(buf + sizeof(struct RegisterBusinessHead));
                ASSERT(packet.datalen >= 
                        sizeof(struct RegisterBusinessHead)+sizeof(struct RegisterBusinessBody)*head->cmd_count
                        ,"RegisterBusinessBody is not enough");
                for(int i = 0; i < head->cmd_count; i++)
                {
                    cmd_manager.register_cmd_for_backend(clientid, body[i].cmd);
                    //body+= sizeof(struct RegisterBusinessBody);
                }
                LOGD("register backend type:%d, cmd count: %d", info.type, head->cmd_count);
                return StreamBuffer();
            }
            else if(packet.cmd == CMD_REGISTER_APP)
            {
                
                #ifdef DEBUG
                    printf("+++++%d+++\n",packet.datalen);
                    for(int i=0;i<packet.datalen;i++)
                    {
                        printf("%d,",buf[i]);
                    }
                    printf("\n-----\n");
                #endif
                
                int user_auth_backend_id = client_manager.get_user_auth_backend();
                if(user_auth_backend_id == ClientManager::UNKNOW_ID)
                {
                    LOGW("userauth backend business is not ready");
                }
                else
                {
                    NetPacket tmppack = packet;
                    BusinessPacket bussiness_packet;
                    bussiness_packet.request_id = clientid;
                    server.sendinl(user_auth_backend_id, PacketDataWithBusiness(tmppack,bussiness_packet,buf,packet.datalen));
                    LOGD("request to register app to clientid:%d", user_auth_backend_id);
                }
            }
            else
            {
                LOGW("unknow cmd 0x%x", packet.cmd);
                return StreamBuffer((unsigned char*)"you are illegal");
            }
        }
        else if(info.type == CLIENT_TYPE_APP) //send to backend
        {
            int target_id = cmd_manager.get_id(packet.cmd);

            if(target_id == CMDManager::NOT_BACKEND_ID)
            {
                LOGW("unknow cmd 0x%x, ignore", packet.cmd);
            }
            else
            {
                //send to backnend
                NetPacket tmppack = packet;
                BusinessPacket bussiness_packet;
                bussiness_packet.request_id = clientid;
                server.sendinl(target_id, PacketDataWithBusiness(tmppack,bussiness_packet,buf,packet.datalen));
            }
        }
        else //if(info.type == backend) //send to app
        {
            if(info.type == CLIENT_TYPE_BUSINESS_USER_AUTH && packet.cmd == CMD_REGISTER_APP_R)
            {
                NetPacket tmppack = packet;
                tmppack.datalen -= sizeof(BusinessPacket);
                ASSERT(packet.datalen >= sizeof(BusinessPacket),"business backend sends illegal message");
                BusinessPacket *bussiness_packet = (BusinessPacket *)buf;
                int request_id = bussiness_packet->request_id;
                unsigned char connect_reply_code;
                ASSERT(packet.datalen >= sizeof(BusinessPacket)+sizeof(connect_reply_code),"unknow user auth reply code");
                connect_reply_code = *(unsigned char*)(buf + sizeof(BusinessPacket));
                if(connect_reply_code == CMD_BUSINESS_CMD_USERAUTH_REPLY_CODE_SUCCESS || 
                    connect_reply_code == CMD_BUSINESS_CMD_USERAUTH_REPLY_CODE_ONLINEALREADY)
                {
                    ClientInfo app_info;
                    if (false == client_manager.get_client_info(request_id, app_info))
                    {
                        LOGE("unknow request_id %d",request_id);
                        return StreamBuffer();
                    }
                    app_info.type = CLIENT_TYPE_APP;
                    client_manager.set_client_info(request_id, app_info);
                    LOGD("app login success %d", request_id);
                }
            }
            {
                NetPacket tmppack = packet;
                tmppack.datalen -= sizeof(BusinessPacket);
                ASSERT(packet.datalen >= sizeof(BusinessPacket),"business backend sends illegal message");
                BusinessPacket *bussiness_packet = (BusinessPacket *)buf;
                int request_id = bussiness_packet->request_id;
                server.sendinl(request_id, PacketData(tmppack,buf+sizeof(BusinessPacket),tmppack.datalen));
            }
        }
        return StreamBuffer();
    }
private:
    StreamBuffer pro_packet_;
};

bool is_eist = false;
int call_time = 0;

void CloseCB(int clientid, void* userdata)
{
    LOGD("cliend %d close\n",clientid);
    TCPServer *theclass = (TCPServer *)userdata;
    //is_eist = true;
    ClientInfo info;
    if (false == client_manager.get_client_info(clientid, info))
    {
        LOGW("unknow clientid %d",clientid);
        return;
    }
    if(info.type != CLIENT_TYPE_UNKNOW || info.type != CLIENT_TYPE_APP)
    {
        cmd_manager.unregister_backend(clientid);
    }
    //g_backend_manager.unregister(clientid);
    client_manager.del_client(clientid);
}

void NewConnect(int clientid, void* userdata)
{
    LOGD("new connect:%d\n",clientid);
    server.SetRecvCB(clientid,NULL,NULL);
    ClientInfo info;
    info.id = clientid;
    info.type = CLIENT_TYPE_UNKNOW;
    client_manager.add_client(clientid, info);
}

int main(int argc, char** argv)
{
    TestTCPProtocol protocol;
    //DeclareDumpFile();
    TCPServer::StartLog("log/");
    server.SetNewConnectCB(NewConnect,&server);
    server.SetClosedCB(CloseCB,&server);
    server.SetPortocol(&protocol);
    if(!server.Start("0.0.0.0",12345)) {
        fprintf(stdout,"Start Server error:%s\n",server.GetLastErrMsg());
    }
    server.SetKeepAlive(1,60);//enable Keepalive, 60s
    fprintf(stdout,"server return on main.\n");
    while(!is_eist) {
        sleep(10);
    }
    return 0;
}
